/**
 * Created by no1 on 2015/7/3.
 */
'use strict';

var DaoBase = require('./DaoBase');
var db = require('./models')
var Model = require('./models/district.js');
var util = require("util");
var F = require('../../function');
var redisCo = F.redisCo;
var debugLog = F.debugLog;

function ModelDao(data) {
  data || (data = {});
  data.model || (data.model = db.district);
  DaoBase.call(this, data);
}
util.inherits(ModelDao, DaoBase);


/**
 * @param district
 redis中存储地区信息
 childrenCode null 表示尚未查询
 '' 表示无children
 '1,2,3'表示children为[1,2,3]
 * @private
 */
function* __saveToRedis(district) {
  // debugLog('---save to redis,code=' + district.code);
  var code = district.code;
  var childrenCode = null; // 默认未查询
  if (district.childrenCode !== undefined) {
    childrenCode = district.childrenCode.join(',');
  }

  yield redisCo.hmset('district:' + code,
    'code', code,
    'pCode', district.pCode,
    'name', district.name,
    'loc', district.loc.join(','),
    'childrenCode', childrenCode
  );

  return true;
}

/**
 * 从redis中读取指定code的地区
 * childrenCode null未查询,[]表示无child,[1,2,3]表示child为1,2,3
 * @param code
 * @returns {*} district对象 or null
 * @private
 */
var hitCount = 0;
var unHitCount = 0;
function* __getFromRedis(code) {
  // debugLog('__getFromRedis,code=' + code);
  var result = yield redisCo.hmget('district:' + code,
    ['code', 'name', 'pCode', 'loc', 'childrenCode']);
  if (!result[0]) { // code
    unHitCount++;
    // debugLog('hit=' + hitCount + ',unHitCount=' + unHitCount);
    return null;
  }
  hitCount++;
  // debugLog('hit=' + hitCount + ',unHitCount=' + unHitCount);
  var childrenCode = null; // null表示尚未查询,[]表示没有child,
  if (result[4] !== null && result[4] !== 'null') {
    childrenCode = result[4] ? result[4].split(',') : [];
  }
  return {
    code: result[0],
    name: result[1],
    pCode: result[2],
    loc: result[3].split(','),
    childrenCode: childrenCode
  }
}

/**
 *
 * @param pCode
 * @param codeList
 * @returns {boolean}
 * @private
 * @return true, false
 */
function* __addChildrenCode(pCode, codeList) {
  // debugLog('__addChildrenCode');
  var result = yield __getFromRedis(pCode);
  if (!result) {
    return false;
  }

  result.childrenCode = codeList;
  yield __saveToRedis(result);
  return true;
}

/**
 * 从数据库中查找
 * @param code
 * @private
 * @return {errors:xx,_doc:xx}
 */
function* __findByCodeInDb(code) {
  // debugLog('__findByCodeInDb');
  try {
    var query = {code: code};
    var select = {__v: 0, ts: 0};
    var result = yield Model.findOne(query, select);
    return {errors: result.errors, _doc: result._doc};
  } catch (e) {
    // debugLog('__findByCodeInDb err', e);
    return {errors: e, _doc: null};
  }
}

/**
 * 从数据库中查询
 * @param pCode
 * @returns {*}
 * @private
 * @return {errors:xx,_docs: doc list}
 */
function* __findByPcodeInDb(pCode) {
  // debugLog('__findByPcodeInDb');
  try {
    var query = {pCode: pCode};
    var select = {__v: 0, ts: 0};
    var result = yield Model.find(query, select);
    if (result.errors) {
      return {errors: result.errors, _docs: null};
    }
    var docs = result.map(function (item) {
      return item._doc;
    });

    return {errors: result.errors, _docs: docs};
  } catch (e) {
    // debugLog('__findByPcodeInDb err', e);
    return {errors: e, _docs: null};
  }
}

/**
 * 首先检测redis,如果不存在,则读数据库,然后写入redis,存在则直接返回
 * @param code
 * @return {errors:xx,_doc: doc}
 * @private
 */
function* __findByCode(code) {
  // debugLog('__findByCode');
  var redisResult = yield __getFromRedis(code);
  if (redisResult) {
    return {errors: null, _doc: redisResult};
  }
  var result = yield __findByCodeInDb(code);
  if (!result.errors) {
    yield  __saveToRedis(result._doc);
  }
  return result;
}

/**
 * TODO 查询节点,并携带直接子节点
 * @return {errors:xx,_doc: doc}
 * @private
 */
function* __findByCodeWithDirectChildren(code) {
  // debugLog('__findByCodeWithDirectChildren');
  var result = yield __findByCode(code);
  if (result.errors) {
    return result;
  }

  var doc = result._doc;
  var sub = [];
  var childrenCode = doc.childrenCode;
  if (typeof childrenCode === 'undefined' || childrenCode === null) { //直接从数据库中来的
    var ret = yield __findByPcodeInDb(code);
    if (ret.errors) {
      return ret;
    }

    childrenCode = [];
    for (var i in ret._docs) {
      var aDoc = ret._docs[i];
      sub.push(aDoc);
      childrenCode.push(aDoc.code);
      yield __saveToRedis(aDoc);
    }

    yield __addChildrenCode(code, childrenCode);

  } else { //已有childrenCode数据,表明可以直接从redis中查询
    for (var j in childrenCode) {
      var aCode = childrenCode[j];
      var subDoc = yield __findByCode(aCode);
      if (subDoc.errors) {
        return subDoc;
      }
      sub.push(subDoc._doc);
    }
  }

  doc.sub = sub;
  return {errors: null, _doc: doc}
}

/**
 * @TODO 查询直接子节点,不携带节点本身
 * @return {errors:xx,_doc: doc}
 * @private
 */
function* __findDirectChildren(pCode) {
  // debugLog('__findDirectChildren,pCode=' + pCode);

  var result = yield __findByCodeWithDirectChildren(pCode);
  if (result.errors) {
    return result;
  }

  var _docs = result._doc.sub;
  return {
    errors: null,
    _docs: _docs
  };
}

/***
 * 内部方法:查询所有child,不携带本身节点
 * @param pCode
 * @return {errors:xx,_doc: doc}
 */
function* __findChildrenByPcode(pCode) {
  // debugLog('__findChildrenByPcode,pCode=' + pCode);
  var result = yield __findDirectChildren(pCode);
  if (result.errors) {
    return result;
  }
  var docs = result._docs;
  var subDoc, subCode, tmpRet;
  for (var i in docs) {
    subDoc = docs[i];
    subCode = subDoc.code;
    tmpRet = yield __findChildrenByPcode(subCode);
    if (tmpRet.errors) {
      return tmpRet;
    } else {
      subDoc.sub = tmpRet._docs;
    }
  }
  return result;
}


/**
 * 删除一个节点，修改标志位 TODO
 * @param code
 * @returns {*}
 */
ModelDao.prototype.deleteByCode = function* (code) {
  // debugLog('deleteByCode');
  try {
    var query = {code: code};
    var update = {status: 3};
    var ret = yield this.model.findOneAndUpdate(query, update);
    if (ret.errors) return F.returnData(1, '查找出错了', ret.errors);
    return F.returnData(0, '成功', result._doc);
  } catch (e) {
    // debugLog('deleteByCode err', e);
    return F.returnData(1, '查找出错了', e);
  }
};

/**
 * 输出过滤
 * 过滤 'childrenCode' 这个字段
 * 过滤 '市辖区'这个字段 如 重庆市市辖区 =>重庆市
 * @param doc
 * @private
 */
function __outputFilter(doc) {
  var list = [];
  if (Array.isArray(doc)) {
    list = list.concat(doc);
  } else {
    list.push(doc);
  }

  while (list.length > 0) {
    var json = list.shift();
    for (var name in json) {
      if (!json.hasOwnProperty(name)) {
        continue;
      }

      if (name === 'name') {
        json[name] = json[name].replace('市辖区', '');
      } else if (name === 'childrenCode') {
        delete json.childrenCode;
      } else if (Object.prototype.toString.call(json[name]) === 'object Object') {
        list.push(json[name]);
      } else if (Array.isArray(json[name])) {
        list = list.concat(json[name]);
      }
    }
  }
}


/**
 * 查询单个节点
 * @param code
 * @returns {*}
 */
ModelDao.prototype.findByCode = function* (code) {
  // debugLog('findByCode');
  var result = yield __findByCode(code);
  if (result.errors) {
    return F.returnData(1, '查找出错了', result.errors);
  } else {
    var doc = result._doc;
    __outputFilter(doc);
    return doc;
  }
};


/**
 * 查询节点+直接child
 * @param code
 * @returns {*}
 */
ModelDao.prototype.findByCodeWithDirectChildren = function* (code) {
  // debugLog('findByCodeWithDirectChildren');
  var result = yield __findByCodeWithDirectChildren(code);
  if (result.errors) {
    return F.returnData(1, '查找出错了', result.errors);
  }

  var doc = result._doc;
  __outputFilter(doc);
  return doc;
};


/**
 * 查询节点 + 所有child
 * @param code
 * @returns {*}
 */
ModelDao.prototype.findByCodeWithAllChildren = function* (code) {
  // debugLog('findByCodeWithAllChildren');
  var result = yield __findByCode(code);
  if (result.errors) {
    return F.returnData(1, '查找出错了', result.errors);
  }

  var sub = yield __findChildrenByPcode(code);
  if (sub.errors) {
    return F.returnData(1, '查找出错了', sub.errors);
  }

  var doc = result._doc;
  doc.sub = sub._docs;
  __outputFilter(doc);
  return doc;
};

/***
 * 搜索整棵树,可能会很耗时，尽量避免这个操作
 * @param code
 * @returns {*}
 */
ModelDao.prototype.findTreeByCode = function* (code) {
  // debugLog('findTreeByCode');
  var result = yield this.findByCodeWithAllChildren(code);
  if (result.errors) {
    return F.returnData(1, '查找出错了', result.errors);
  }

  // 查找父节点
  var doc = result;
  var pDoc;
  var pCode = doc.pCode;
  if (pCode != 0) { // this is root
    while (true) {
      var parent = yield __findByCode(pCode);//{errors:xx,_doc: doc}
      if (parent.errors) {
        return F.returnData(1, '查找出错了', parent.errors);
      }
      pDoc = parent._doc;
      if (pDoc) {
        pDoc.sub = doc;
        doc = pDoc;
        pCode = doc.pCode;
        if (pCode <= 0) {
          break;
        }
      } else {
        break;
      }
    }
  }

  __outputFilter(doc);
  return doc;
};

/**
 * @todo 保存
 * @param aJson
 * @returns {*}
 */
ModelDao.prototype.save = function* (aJson) {
  var item = new this.model(aJson);
  try {
    var result = yield item.save();
    if (result.errors) return F.returnData(1, 'save出错了', result.errors);
    return F.returnData(0, '保存成功', result);
  } catch (e) {
    return F.returnData(1, 'save出错了', e);
  }
};

/**
 * 根据距离,进行查找
 *
 * @param cord
 * @param radius
 * @param limit
 * @param skip
 */
ModelDao.prototype.findNear = function* (cord, radius, limit, skip) {
  // debugLog('findNearV2');
  if (typeof skip === 'undefined') skip = 0;
  //var max = __toRadius(radius); // maxDistance单位是m ??
  var max = radius;
  // debugLog(max);
  var match = {
    "$geoNear": {
      'near': cord,
      'spherical': true,
      'distanceField': 'dist',
      'includeLocs': 'loc',
      'maxDistance': max
    }
  };
  var result = yield this.model.aggregate([match, {"$skip": skip}, {"$limit": limit}]).exec();
  result.forEach(function (item) {
    item.dist = __toMeter(item.dist);
  });

  return result;
};

//var earthRadius = 6371; // km, miles is 3959
var earthRadius = 6371000; // m
function __toRadius(meter) {
  return meter / earthRadius;
}
function __toMeter(r) {
  var d = r * earthRadius;
  return F.toFixed(1);
}

/**
 * 查询城市列表
 * @param pCode
 */
ModelDao.prototype.findCity = function*(pCode) {
  // debugLog('findCity');
  try {
    var query = {level: 2};
    if (typeof pCode !== 'undefined') {
      query.pCode = pCode;
    }
    var select = {__v: 0, ts: 0};
    var result = yield this.model.find(query, select).exec();
    if (result.errors) {
      return F.returnData(1, '查找出错了', result.errors);
    }
    var list = result.map(function (item) {
      return item._doc;
    });
    __outputFilter(list);
    return list;
  } catch (e) {
    return F.returnData(1, '查找出错了', e);
  }
};

/**
 * 查询城市省+直辖市
 * @param pCode
 */
ModelDao.prototype.findProvice = function*(pCode) {
  // debugLog('findProvice');
  try {
    var query = {level: 1};
    var select = {__v: 0, ts: 0};
    var result = yield this.model.find(query, select);
    if (result.errors) {
      return F.returnData(1, '查找出错了', result.errors);
    }
    var list = result.map(function (item) {
      return item._doc;
    });
    __outputFilter(list);
    return list;
  } catch (e) {
    return F.returnData(1, '查找出错了', e);
  }
};

ModelDao.prototype.findBypCode = function*(pCode, opt) {
  let query = {};
  query.pCode = pCode;
  var ret = yield this.model.find(query, opt);
  if (!ret) return F.returnData(1, '没有找到');
  if (ret.errors) return F.returnData(1, ret.errors, ret);
  let data = [];
  if (ret instanceof Array) {
    for (let i in ret) {
      data[i] = ret[i]._doc;
    }
  }
  return F.returnData(0, '查询成功', data);
}

module.exports = ModelDao;
